import random
from random import randint

import gym

from Examples.DQNExamples.SlayTheSpire.SlayTheSpireBase import Player
from Examples.DQNExamples.SlayTheSpire.SlayTheSpireCards import all_cards
from Examples.DQNExamples.SlayTheSpire.SlayTheSpireMonster import Cultist


def remove_defend(player, monster):
    if not player.buffer[6]:
        player.defend = 0
    if not monster.buffer[6]:
        monster.defend = 0


def remove_debuff(player, monster):
    for i in range(2, 5):
        if player.buffer[i] > 0:
            player.buffer[i] -= 1
        if monster.buffer[i] > 0:
            monster.buffer[i] -= 1


def add_strength(player, monster):
    player.buffer[0] += player.buffer[5]
    monster.buffer[0] += monster.buffer[5]


ALL_BUFF = ['力量', '敏捷', '虚弱', '脆弱', '易伤', '仪式', '壁垒']
TRIGGER = {
    'before_turn': [],  # 回合开始前
    'draw_card': [],  # 抽牌时
    'play_card': [],  # 出牌时
    'exhaust_card': [],  # 消耗牌时
    'drop_card': [],  # 弃牌时
    'turn_over': [],  # 回合结束时
    'monster_action_before': [],  # 怪物回合开始时
    'monster_action': [],  # 怪物行动
    'monster_action_down': [
    ],  # 怪物行动结束
}


class SlayTheSpireEnv(gym.Env):
    def __init__(self, player, monsters, cards):
        self.player = player
        self.monsters = monsters
        self.cards = cards
        # self.action_space = gym.spaces.Discrete(3)
        # self.observation_space = gym.spaces.Discrete(1004)
        self.turn_num = 1
        self.turn = 'battle_before'

    def reset(self, *args, **kwargs):
        self.reset_trigger()
        self.player.reset()
        for monster in self.monsters:
            monster.reset()
        self.turn_num = 1
        start_deck = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3]
        random.shuffle(start_deck)
        self.player.draw_deck[:11] = start_deck
        self.turn = 'battle_before'
        return self.get_obs()

    def reset_trigger(self):
        TRIGGER['monster_action_down'] = [
            {'trigger': remove_defend, 'trigger_args': (self.player, self.monsters[0])},
            {'trigger': remove_debuff, 'trigger_args': (self.player, self.monsters[0])},
            {'trigger': add_strength, 'trigger_args': (self.player, self.monsters[0])}
        ]

    def step(self, action):
        action_card = action
        action_down = action == 10
        # 执行动作

        if self.turn == 'battle_before':
            # 回合开始前
            self.player.mp = self.player.max_mp
            before_turn = TRIGGER['before_turn']
            self.turn = 'before_turn'
            for trigger in before_turn:
                trigger.get('trigger')(*trigger.get('trigger_args'))
            # 抽牌时
            draw_card = TRIGGER['draw_card']
            for trigger in draw_card:
                trigger.get('trigger')(*trigger.get('trigger_args'))
            self.player.draw_card()
            # self.log()
        # 出牌
        reward = self.player.play_card(action_card, self.cards, self.monsters[0], self)
        if reward == 0:
            # self.log()
            pass

        all_hand_cards = [all_cards.get(i) for i in self.player.hand_card if i != 0]
        no_power = all(i.cost > self.player.mp for i in all_hand_cards)

        if action_down or no_power:
            # 回合结束时
            turn_over = TRIGGER['turn_over']
            for trigger in turn_over:
                trigger.get('trigger')(*trigger.get('trigger_args'))
            self.player.drop_card()
            # 怪物回合开始时
            monster_action_before = TRIGGER['monster_action_before']
            for trigger in monster_action_before:
                trigger.get('trigger')(*trigger.get('trigger_args'))
            # 怪物行动
            for monster in self.monsters:
                monster.action(self.turn_num, TRIGGER, self.player)
            # 怪物行动结束
            monster_action_down = TRIGGER['monster_action_down']
            for trigger in monster_action_down:
                trigger.get('trigger')(*trigger.get('trigger_args'))
            self.turn_num += 1
            self.reset_trigger()
            self.turn = 'monster_action_down'
            down = False
            if self.player.hp <= 0:
                down = True
                reward = -self.monsters[0].hp
            elif all(monster.hp <= 0 for monster in self.monsters):
                down = True
                reward = self.player.hp
            else:
                reward = 0
                self.player.mp = self.player.max_mp
                self.player.draw_card()
            obs = self.get_obs()
            return obs, reward, down, {}

        down = False
        if self.player.hp <= 0:
            down = True
            reward = -self.monsters[0].hp
        if all(monster.hp <= 0 for monster in self.monsters):
            down = True
            reward = self.player.hp
        obs = self.get_obs()
        return obs, reward, down, {}

    def get_obs(self):
        obs = []
        obs.append(self.player.hp)
        obs.append(self.monsters[0].hp)
        obs.append(self.player.mp)
        draw_deck = []
        draw_deck_tmp = []
        for i in self.player.draw_deck:
            draw_deck_tmp.append(i)
        random.shuffle(draw_deck_tmp)
        for i in draw_deck_tmp:
            if i == 0:
                draw_deck.extend([0, 0, 0])
            if i == 1:
                draw_deck.extend([1, 0, 0])
            if i == 2:
                draw_deck.extend([0, 1, 0])
            if i == 3:
                draw_deck.extend([0, 0, 1])
        obs.extend(draw_deck)
        # obs.extend(self.player.draw_deck)
        drop_deck = []
        for i in self.player.drop_deck:
            if i == 0:
                drop_deck.extend([0, 0, 0])
            if i == 1:
                drop_deck.extend([1, 0, 0])
            if i == 2:
                drop_deck.extend([0, 1, 0])
            if i == 3:
                drop_deck.extend([0, 0, 1])
        obs.extend(drop_deck)
        # obs.extend(self.player.drop_deck)
        hand_card = []
        for i in self.player.hand_card:
            if i == 0:
                hand_card.extend([0, 0, 0])
            if i == 1:
                hand_card.extend([1, 0, 0])
            if i == 2:
                hand_card.extend([0, 1, 0])
            if i == 3:
                hand_card.extend([0, 0, 1])
        obs.extend(hand_card)

        # obs.extend(self.player.hand_card)
        obs.extend(self.player.buffer)
        obs.extend(self.monsters[0].buffer)
        obs.append(self.monsters[0].base_damage)
        obs.append(self.monsters[0].attack_num)
        return obs

    def log(self):
        print()
        print()
        print('------------------')
        print(f'回合数：{self.turn_num}')
        print(f'抽牌堆: {list(filter(lambda x: x != 0, self.player.draw_deck))}')
        print(f'弃牌堆: {list(filter(lambda x: x != 0, self.player.drop_deck))}')
        print(f'手牌: {list(filter(lambda x: x != 0, self.player.hand_card))}')
        print(f'能量: {self.player.mp}')
        print(f'生命值: {self.player.hp}')
        print(f'护甲: {self.player.defend}')
        print(f'玩家buff: {self.player.buffer}')
        print(f'怪物buff: {self.monsters[0].buffer}')
        print(f'怪物攻击: {self.monsters[0].base_damage + self.monsters[0].buffer[0]} * {self.monsters[0].attack_num}')
        print(f'怪物生命值: {self.monsters[0].hp}')
        pass


if __name__ == '__main__':
    player = Player(max_hp=100, max_mp=3)
    monster = Cultist()
    cards = all_cards
    game = SlayTheSpireEnv(player=player, monsters=[monster], cards=cards)
    game.reset()
    game.log()
    for i in range(200):
        card_no = randint(0, 10)
        obs, reward, down, info = game.step(card_no)
        if down:
            game.log()
            break
        print(card_no)
